/*
	The original MD2 code was modified to calculate vertex normals and interpolate
	normals for animation.  Original info below:



 *  Texture.cpp
 *  MD2 File Loading
 *
 *  Created by Seth Willits on 9/11/04.
 *  Copyright 2004 Freak Software. All rights reserved.
 *
 *  Portions of this file are from the MD2 file loading routines
 *  found in Chapter 18 of OpenGL Game Programming by Kevin Hawkins
 *  and Dave Astle. 
 *  This includes the vector operations, SetupSkin, CalculateNormal,
 *  and the Draw function.
 */
 
#include "MD2.h"
#include <iostream>
#include <cmath>

using namespace std;


// vector subtraction
MD2Vector operator-(MD2Vector a, MD2Vector b)
{
	MD2Vector c;

	c.point[0] = a.point[0] - b.point[0];
	c.point[1] = a.point[1] - b.point[1];
	c.point[2] = a.point[2] - b.point[2];

	return c;
}

// scalar-vector multiplication
MD2Vector operator*(float f, MD2Vector b)
{
	MD2Vector c;

	c.point[0] = f * b.point[0];
	c.point[1] = f * b.point[1];
	c.point[2] = f * b.point[2];

	return c;
}

// vector division
MD2Vector operator/(MD2Vector a, MD2Vector b)
{
	MD2Vector c;

	c.point[0] = a.point[0] / b.point[0];
	c.point[1] = a.point[1] / b.point[1];
	c.point[2] = a.point[2] / b.point[2];

	return c;
}


// vector addition
MD2Vector operator+(MD2Vector a, MD2Vector b)
{
	MD2Vector c;

	c.point[0] = a.point[0] + b.point[0];
	c.point[1] = a.point[1] + b.point[1];
	c.point[2] = a.point[2] + b.point[2];

	return c;
}


// MD2Model constructor
MD2Model::MD2Model()
{
	NumVertices = 0;		// vertices
	NumTriangles = 0;		// triangles
	NumFrames = 0;			// frames
	NumTexCoords = 0;		// texture coordinates
	FrameSize = 0;			// needed?
	TrianglesIndex = NULL;   // triangle indices
	TextureCoords = NULL;	// texture coordinate indices
	VertexList = NULL;		// vertices
	normalList = NULL;		// normals
	ModelTex = NULL;		// skin/texture
}    

// MD2Model destructor
MD2Model::~MD2Model()
{
	if (TrianglesIndex != NULL) free(TrianglesIndex);
	if (VertexList != NULL) free(VertexList);
	if (normalList != NULL) free(normalList);
	//if (normalList != NULL) delete[] normalList;
	if (TextureCoords != NULL) free(TextureCoords);
}

// MD2Model::SetupSkin()
// access: private
// desc: sets up the model skin/texture for OpenGL
void MD2Model::SetupSkin(MD2Texture *thisTexture)
{
	// set the proper parameters for an MD2 texture
	glGenTextures(1, (GLuint*) &(thisTexture->texID));
	glBindTexture(GL_TEXTURE_2D, thisTexture->texID);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

	
	switch (thisTexture->textureType)
	{
		case PCX:
			gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, thisTexture->width, thisTexture->height, GL_RGBA, GL_UNSIGNED_BYTE, thisTexture->data);
		default:
			break;
	}
}
 

short MD2Model::ReadShort(FILE *filePtr)
{
	short value = 0;
	fread(&value, sizeof(short), 1, filePtr);
	

	
	return value;
}


int MD2Model::ReadInt(FILE *filePtr)
{
	int value = 0;
	fread(&value, sizeof(int), 1, filePtr);
	

	
	return value;
}


float MD2Model::ReadFloat(FILE *filePtr)
{
	float value = 0;
	fread(&value, sizeof(float), 1, filePtr);
	

	
	return value;
}


void MD2Model::ReadHeader(FILE *filePtr, MD2ModelHeader & header)
{
	// Go to Beginning of File
	fseek(filePtr, 0, SEEK_SET);
	
	// Read Header
	header.ident = ReadInt(filePtr);
	header.version = ReadInt(filePtr);
	header.skinwidth = ReadInt(filePtr);
	header.skinheight = ReadInt(filePtr);
	header.framesize = ReadInt(filePtr);
	header.NumSkins = ReadInt(filePtr);
	header.NumVertices = ReadInt(filePtr);
	header.NumTexCoords = ReadInt(filePtr);
	header.NumTriangles = ReadInt(filePtr);
	header.NumGLCmds = ReadInt(filePtr);
	header.NumFrames = ReadInt(filePtr);
	header.offsetSkins = ReadInt(filePtr);
	header.offsetST = ReadInt(filePtr);
	header.offsetTriangles = ReadInt(filePtr);
	header.offsetFrames = ReadInt(filePtr);
	header.offsetGLCmds = ReadInt(filePtr);
	header.offsetEndOfFile = ReadInt(filePtr);
}


// MD2Model::Load()
// access: public
// desc: loads model and skin
bool MD2Model::Load(char *modelFile, char *skinFile)
{
	FILE *filePtr; 				// file pointer
	int fileLength;				// length of model file
	MD2ModelHeader header;		// model header
	MD2Frame frame;				// frame data
	MD2Vector * vectors;		// 
	int i, j;					// index variables
	
	
	// Open the Model File
	filePtr = fopen(modelFile, "rb");
	if (filePtr == NULL)
		return false;
	
	
	// Find Length of File
	fseek(filePtr, 0, SEEK_END);
	fileLength = ftell(filePtr);
	
	
	// Extract model file header from buffer
	ReadHeader(filePtr, header);
	
	
	// Save Some Header Information
	NumVertices = header.NumVertices;
	NumFrames = header.NumFrames;
	FrameSize = header.framesize;
	NumTexCoords = header.NumTexCoords;
	NumTriangles = header.NumTriangles;
	
	
	// Create Vertex List
	VertexList = new MD2Vector [NumVertices * NumFrames];
	
	// For Each Frame
	frame.fp = NULL;
	for (j = 0; j < NumFrames; j++) {
		
		// Move to Frame in File
		fseek(filePtr, header.offsetFrames + FrameSize * j, SEEK_SET);
		
		// Read Frame
		// framePtr = (MD2Frame*) &(buffer[header.offsetFrames + FrameSize * j]);
		frame.scale[0] = ReadFloat(filePtr);
		frame.scale[1] = ReadFloat(filePtr);
		frame.scale[2] = ReadFloat(filePtr);
		frame.translate[0] = ReadFloat(filePtr);
		frame.translate[1] = ReadFloat(filePtr);
		frame.translate[2] = ReadFloat(filePtr);
		fread(&frame.name, sizeof(char), 16, filePtr);
		if (frame.fp != NULL) free(frame.fp);
		frame.fp = (MD2FramePoint *) calloc(NumVertices, sizeof(MD2FramePoint));
		fread(frame.fp, sizeof(MD2FramePoint), NumVertices, filePtr);
		
		
		// Save the Vertices
		vectors = (MD2Vector*) &VertexList[NumVertices * j];
		for (i = 0; i < NumVertices; i++) {
			
			VertexList[NumVertices * j + i].point[0] = frame.scale[0] * frame.fp[i].v[0] + frame.translate[0];
			VertexList[NumVertices * j + i].point[1] = frame.scale[1] * frame.fp[i].v[1] + frame.translate[1];
			VertexList[NumVertices * j + i].point[2] = frame.scale[2] * frame.fp[i].v[2] + frame.translate[2];
		}
	}
	
	
	// Textures
	ModelTex = LoadTexture(skinFile);
	if (ModelTex != NULL)
		SetupSkin(ModelTex);
	else
		return false;
	
	
	// Create Texture Coords
	TextureCoords = new MD2TextureCoord [NumTexCoords];
		
	// Move to Texture Coordinates in File
	fseek(filePtr, header.offsetST, SEEK_SET);
	
	// Read in Texture Coordinate List
	for (i = 0; i < NumTexCoords; i++) {
		TextureCoords[i].s = (float)ReadShort(filePtr) / (float)ModelTex->width;
		TextureCoords[i].t = (float)ReadShort(filePtr) / (float)ModelTex->height;
	}
	
	
	
	
	// Triangles
	TrianglesIndex = new MD2Mesh [NumFrames * NumTriangles];
	
	// Move to Triangles in File
	// bufIndexPtr = (MD2Mesh*)&buffer[header.offsetTriangles];
	fseek(filePtr, header.offsetTriangles, SEEK_SET);
	
	// Create a Mesh (triangle) List
	for (i = 0; i < NumTriangles; i++) {
		TrianglesIndex[i].meshIndex[0] = ReadShort(filePtr);
		TrianglesIndex[i].meshIndex[1] = ReadShort(filePtr);
		TrianglesIndex[i].meshIndex[2] = ReadShort(filePtr);
		TrianglesIndex[i].stIndex[0] = ReadShort(filePtr);
		TrianglesIndex[i].stIndex[1] = ReadShort(filePtr);
		TrianglesIndex[i].stIndex[2] = ReadShort(filePtr);
	}
	
	
	// Close File and Free Memory
	fclose(filePtr);
	
	// CUSTOM NORMAL CALC CODE!!!  (and collision sphere generation) -- could also make rectangle

	// Initialize normal list

	normalList = new MD2Vector [NumVertices * NumFrames];
	for (int f = 0; f < NumFrames; f++)
	{
		//Loop through each frame, add normals
		for (int i = f * NumVertices; i < (f*NumVertices) + NumVertices; i++)
		{
			MD2Vector temp;
			temp.point[0] = 0;
			temp.point[1] = 0;
			temp.point[2] = 0;
			MD2Vector currentV = VertexList[i];

			for (int j = 0; j < NumTriangles; j++)
			{
				unsigned short* vertexIndeces = TrianglesIndex[j].meshIndex;
				for (int k = 0; k < 3; k++)
				{
					MD2Vector v = VertexList[(f*NumVertices) + vertexIndeces[k]];
					if (v.point[0] == currentV.point[0] &&
						v.point[1] == currentV.point[1] &&
						v.point[2] == currentV.point[2])
					{
						// Calculate the polygon normal, add it
						float* p1;
						float* p2;
						float* p3;
						p1 = VertexList[(f*NumVertices) + vertexIndeces[0]].point;
						p2 = VertexList[(f*NumVertices) + vertexIndeces[2]].point;
						p3 = VertexList[(f*NumVertices) + vertexIndeces[1]].point;
						float a[3], b[3], result[3];
						//float length;

						a[0] = p1[0] - p2[0];
						a[1] = p1[1] - p2[1];
						a[2] = p1[2] - p2[2];

						b[0] = p1[0] - p3[0];
						b[1] = p1[1] - p3[1];
						b[2] = p1[2] - p3[2];

						result[0] = a[1] * b[2] - b[1] * a[2];
						result[1] = b[0] * a[2] - a[0] * b[2];
						result[2] = a[0] * b[1] - b[0] * a[1];

						temp.point[0] += result[0];
						temp.point[1] += result[1];
						temp.point[2] += result[2];
						break;
					}
				}

			}

			// NORMAL IS NOW TEMP!!!
			//normalList[i] = temp;
			normalList[i].point[0] = temp.point[0];
			normalList[i].point[1] = temp.point[1];
			normalList[i].point[2] = temp.point[2];

			//cout << "adding normal " << temp.point[0] << ", " << temp.point[1] << ", " << temp.point[2] << endl;

		}
	}

	/*

	normalList = new MD2Vector [NumVertices * NumFrames];
	int allVertsSize = NumVertices * NumFrames;
	for (int i = 0; i < allVertsSize; i++)
	{
		MD2Vector temp;
		temp.point[0] = 0;
		temp.point[1] = 0;
		temp.point[2] = 0;
		MD2Vector currentV = VertexList[i];

		for (int j = 0; j < NumTriangles; j++)
		{
			unsigned short* vertexIndeces = TrianglesIndex[j].meshIndex;
			for (int k = 0; k < 3; k++)
			{
				MD2Vector v = VertexList[vertexIndeces[k]];
				if (v.point[0] == currentV.point[0] &&
					v.point[1] == currentV.point[1] &&
					v.point[2] == currentV.point[2])
				{
					// Calculate the polygon normal, add it
					float* p1;
					float* p2;
					float* p3;
					p1 = VertexList[vertexIndeces[0]].point;
					p2 = VertexList[vertexIndeces[2]].point;
					p3 = VertexList[vertexIndeces[1]].point;
					float a[3], b[3], result[3];
					//float length;

				   a[0] = p1[0] - p2[0];
				   a[1] = p1[1] - p2[1];
				   a[2] = p1[2] - p2[2];

				   b[0] = p1[0] - p3[0];
				   b[1] = p1[1] - p3[1];
				   b[2] = p1[2] - p3[2];

				   result[0] = a[1] * b[2] - b[1] * a[2];
				   result[1] = b[0] * a[2] - a[0] * b[2];
				   result[2] = a[0] * b[1] - b[0] * a[1];

					temp.point[0] += result[0];
					temp.point[1] += result[1];
					temp.point[2] += result[2];
					break;
				}
			}

		}

		// NORMAL IS NOW TEMP!!!
		//normalList[i] = temp;
		normalList[i].point[0] = temp.point[0];
		normalList[i].point[1] = temp.point[1];
		normalList[i].point[2] = temp.point[2];

		cout << "adding normal " << temp.point[0] << ", " << temp.point[1] << ", " << temp.point[2] << endl;
	}*/

	// File Successfuly Loaded
	return true;
}




///////////////////////////////////////////////////////////////////////////
//	CalculateNormal - given 3 points, calculates the normal to the points
//	
///////////////////////////////////////////////////////////////////////////

void MD2Model::CalculateNormal( float *p1, float *p2, float *p3 )
{
   float a[3], b[3], result[3];
   float length;

   a[0] = p1[0] - p2[0];
   a[1] = p1[1] - p2[1];
   a[2] = p1[2] - p2[2];

   b[0] = p1[0] - p3[0];
   b[1] = p1[1] - p3[1];
   b[2] = p1[2] - p3[2];

   result[0] = a[1] * b[2] - b[1] * a[2];
   result[1] = b[0] * a[2] - a[0] * b[2];
   result[2] = a[0] * b[1] - b[0] * a[1];

   // calculate the length of the normal
   length = (float)sqrt(result[0]*result[0] + result[1]*result[1] + result[2]*result[2]);

   // normalize and specify the normal
   glNormal3f(result[0]/length, result[1]/length, result[2]/length);
}


// MD2Model::Draw()
// access: public
// desc: animates the model between the keyframes startFrame and endFrame
void MD2Model::Draw(int CurrentFrame, int NextFrame, float Interpolation)
{

	MD2Vector *vList;    		// current frame vertices
	MD2Vector *nextVList;		// next frame vertices
	int i;    					// index counter
	float x1, y1, z1;   		// current frame point values
	float x2, y2, z2;			// next frame point values
	MD2Vector vertex[3]; 

	//
	MD2Vector normal[3];
	MD2Vector* nList;
	MD2Vector* nextNList;
	float nx1,ny1,nz1;
	float nx2,ny2,nz2;
	
	
	
	// Get Vertex list at offset for the current frame
	vList = &VertexList[NumVertices*CurrentFrame];

	//NORMALS
	nList = &normalList[NumVertices*CurrentFrame];
	
	// Get vertex list at offset for the next frame
	nextVList = &VertexList[NumVertices*NextFrame];

	//NEXT NORMALS
	nextNList = &normalList[NumVertices*NextFrame];			// NOT THE SAME AS ARRAY!!!?
	
	// Pick our texture
	glBindTexture(GL_TEXTURE_2D, ModelTex->texID);
	
	
	// Draw the Model
	glBegin(GL_TRIANGLES);
	for (i = 0; i < NumTriangles; i++) {
		
		// get first points of each frame
		x1 = vList[TrianglesIndex[i].meshIndex[0]].point[0];
		y1 = vList[TrianglesIndex[i].meshIndex[0]].point[1];
		z1 = vList[TrianglesIndex[i].meshIndex[0]].point[2];
		x2 = nextVList[TrianglesIndex[i].meshIndex[0]].point[0];
		y2 = nextVList[TrianglesIndex[i].meshIndex[0]].point[1];
		z2 = nextVList[TrianglesIndex[i].meshIndex[0]].point[2];
		//

		
		//MD2Vector vecx  = nList[TrianglesIndex[i].meshIndex[0]];

		nx1 = nList[TrianglesIndex[i].meshIndex[0]].point[0];
		ny1 = nList[TrianglesIndex[i].meshIndex[0]].point[1];
		nz1 = nList[TrianglesIndex[i].meshIndex[0]].point[2];
		nx2 = nextNList[TrianglesIndex[i].meshIndex[0]].point[0];
		ny2 = nextNList[TrianglesIndex[i].meshIndex[0]].point[1];
		nz2 = nextNList[TrianglesIndex[i].meshIndex[0]].point[2];
		
		// store first interpolated vertex of triangle
		vertex[0].point[0] = x1 + Interpolation * (x2 - x1);
		vertex[0].point[1] = y1 + Interpolation * (y2 - y1);
		vertex[0].point[2] = z1 + Interpolation * (z2 - z1);
		//
		normal[0].point[0] = nx1 + Interpolation * (nx2 - nx1);
		normal[0].point[1] = ny1 + Interpolation * (ny2 - ny1);
		normal[0].point[2] = nz1 + Interpolation * (nz2 - nz1);
		
		// get second points of each frame
		x1 = vList[TrianglesIndex[i].meshIndex[2]].point[0];
		y1 = vList[TrianglesIndex[i].meshIndex[2]].point[1];
		z1 = vList[TrianglesIndex[i].meshIndex[2]].point[2];
		x2 = nextVList[TrianglesIndex[i].meshIndex[2]].point[0];
		y2 = nextVList[TrianglesIndex[i].meshIndex[2]].point[1];
		z2 = nextVList[TrianglesIndex[i].meshIndex[2]].point[2];
		//
		nx1 = nList[TrianglesIndex[i].meshIndex[2]].point[0];
		ny1 = nList[TrianglesIndex[i].meshIndex[2]].point[1];
		nz1 = nList[TrianglesIndex[i].meshIndex[2]].point[2];
		nx2 = nextNList[TrianglesIndex[i].meshIndex[2]].point[0];
		ny2 = nextNList[TrianglesIndex[i].meshIndex[2]].point[1];
		nz2 = nextNList[TrianglesIndex[i].meshIndex[2]].point[2];
		
		// store second interpolated vertex of triangle
		vertex[2].point[0] = x1 + Interpolation * (x2 - x1);
		vertex[2].point[1] = y1 + Interpolation * (y2 - y1);
		vertex[2].point[2] = z1 + Interpolation * (z2 - z1);   
		//
		normal[2].point[0] = nx1 + Interpolation * (nx2 - nx1);
		normal[2].point[1] = ny1 + Interpolation * (ny2 - ny1);
		normal[2].point[2] = nz1 + Interpolation * (nz2 - nz1);
		
		// get third points of each frame
		x1 = vList[TrianglesIndex[i].meshIndex[1]].point[0];
		y1 = vList[TrianglesIndex[i].meshIndex[1]].point[1];
		z1 = vList[TrianglesIndex[i].meshIndex[1]].point[2];
		x2 = nextVList[TrianglesIndex[i].meshIndex[1]].point[0];
		y2 = nextVList[TrianglesIndex[i].meshIndex[1]].point[1];
		z2 = nextVList[TrianglesIndex[i].meshIndex[1]].point[2];
		//
		nx1 = nList[TrianglesIndex[i].meshIndex[1]].point[0];
		ny1 = nList[TrianglesIndex[i].meshIndex[1]].point[1];
		nz1 = nList[TrianglesIndex[i].meshIndex[1]].point[2];
		nx2 = nextNList[TrianglesIndex[i].meshIndex[1]].point[0];
		ny2 = nextNList[TrianglesIndex[i].meshIndex[1]].point[1];
		nz2 = nextNList[TrianglesIndex[i].meshIndex[1]].point[2];
		
		// store third interpolated vertex of triangle
		vertex[1].point[0] = x1 + Interpolation * (x2 - x1);
		vertex[1].point[1] = y1 + Interpolation * (y2 - y1);
		vertex[1].point[2] = z1 + Interpolation * (z2 - z1);
		//
		normal[1].point[0] = nx1 + Interpolation * (nx2 - nx1);
		normal[1].point[1] = ny1 + Interpolation * (ny2 - ny1);
		normal[1].point[2] = nz1 + Interpolation * (nz2 - nz1);
		
		// calculate the normal of the triangle
		// DONT CALCULATE HERE!!!
		//CalculateNormal(vertex[0].point, vertex[2].point, vertex[1].point);
		
		// render properly textured triangle
		glNormal3fv(normal[0].point);
		glTexCoord2f(TextureCoords[TrianglesIndex[i].stIndex[0]].s, TextureCoords[TrianglesIndex[i].stIndex[0]].t);
		glVertex3fv(vertex[0].point);
		
		glNormal3fv(normal[2].point);
		glTexCoord2f(TextureCoords[TrianglesIndex[i].stIndex[2]].s, TextureCoords[TrianglesIndex[i].stIndex[2]].t);
		glVertex3fv(vertex[2].point);
		
		glNormal3fv(normal[1].point);
		glTexCoord2f(TextureCoords[TrianglesIndex[i].stIndex[1]].s, TextureCoords[TrianglesIndex[i].stIndex[1]].t);
		glVertex3fv(vertex[1].point);
	}
	glEnd();
}


